home *** CD-ROM | disk | FTP | other *** search
- /*______________________________________________________*/
- /* Sound I/O Demo */
- /* by */
- /* RICHARD P. COLLYER */
- /* Developer Technical Support */
- /* Apple Computer, Inc. */
- /* 11/14/91 */
- /*______________________________________________________*/
-
- /*
-
-
- */
-
- /**********************************/
- /* #includes */
- /**********************************/
-
- #include "2BufRecordToBufCmd.h"
- #include "atob.h"
- #include "PlayMovie.h"
-
- /**********************************/
- /* Application Globals */
- /**********************************/
-
- SPBPtr gRecordStruct;
- Handle gBufferHandle[kNumberOfBuffers];
- Fixed gSampleRate;
- long gInternalBuffer;
- long gSoundRefNum = 0;
- unsigned long gSampleAreaSize; // size of the sample area in the snd handle
- int gHeaderSize; // Size of the Header to be skipped in for the bufferCmd
- short gDataStart; // Size of the Entire 'snd ' header
- short gSampleSize;
- short gNumberOfChannels;
- short gWhichRecordBuffer = 0;
- OSErr gError;
- OSType gCompression;
-
- short gWaitingToSend = 0;
- short gSendingData = 0;
- short gNetBufferSize = 0;
- short gStandardBuffSize;
- Ptr gNetBuffers[kNumberOfBuffers];
- Ptr gCurrentNetBufferPoint;
- short gCurrentNetBuffer;
-
-
- /**********************************/
- /* TestTheSystem */
- /**********************************/
-
- void TestTheSystem (void)
-
- /* use Gestalt to make sure the app will work on the system*/
-
- {
- long feature;
-
- gError = Gestalt(gestaltSoundAttr, &feature);
- if (!gError) {
- /* First Check to see that Sound Input is available */
- if ( !(feature & (1 << gestaltHasSoundInputDevice)) )
- ExitWithMessage ("No Sound Input Device, so The app can't run", 0);
-
- /* Second Check to see that the hardware supports stereo, if not then I can't
- record and play sounds at the same time */
- if ( !(feature & (1 << gestaltStereoCapability)) )
- ExitWithMessage ("No Stereo support, so The app can't run", 0);
- }
- else
- ExitWithMessage ("Gestalt failed in TestTheSystem", gError);
-
- return;
- }
-
- /**********************************/
- /* GetSoundDeviceInfo */
- /**********************************/
-
- void GetSoundDeviceInfo (void)
-
- /* Extract the information about the Sound Input Device to build the Sound Header */
-
- {
- long value;
-
- /* Get the sample rate information for the snd header */
-
- gSampleRate = 0x56EE8BA3; /* 11 KHz = 0x2B7745D1 22 KHz = 0x56EE8BA3*/
-
- gError = SPBSetDeviceInfo (gSoundRefNum,siSampleRate, (Ptr) &gSampleRate);
- if (gError != noErr)
- ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siSampleRate", gError);
-
- gError = SPBGetDeviceInfo (gSoundRefNum,siSampleRate, (Ptr) &gSampleRate);
- if (gError != noErr)
- ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siSampleRate", gError);
-
- /* Get the sample size information for the snd header */
-
- gError = SPBGetDeviceInfo (gSoundRefNum,siSampleSize, (Ptr) &gSampleSize);
- if (gError != noErr)
- ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siSampleSize", gError);
-
- /* Get the compression type information for the snd header */
-
- gError = SPBGetDeviceInfo (gSoundRefNum,siCompressionType, (Ptr) &gCompression);
- if (gError != noErr)
- ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siCompressionType", gError);
-
- /* Get the number of input channels for the snd header */
-
- gError = SPBGetDeviceInfo (gSoundRefNum,siNumberChannels, (Ptr) &gNumberOfChannels);
- if (gError != noErr)
- ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siNumberChannels", gError);
-
- /* Get the size of the internal sound buffer for the snd buffer */
-
- gError = SPBGetDeviceInfo (gSoundRefNum,siDeviceBufferInfo, (Ptr) &gInternalBuffer);
- if (gError != noErr)
- ExitWithMessage ("SPBGetDeviceInfo failed in GetSoundDeviceInfo while looking at siDeviceBufferInfo", gError);
-
- value = kMilliSecondsOfSound;
- gError = SPBMillisecondsToBytes(gSoundRefNum, &value);
- if (gError != noErr)
- ExitWithMessage ((char *)"\pSPBMillisecondsToBytes failed in GetSoundDeviceInfo", gError);
-
- /* Round the buffer size to a multiple of the internal buffer size */
- gSampleAreaSize = (value / gInternalBuffer) * gInternalBuffer;
-
- return;
- }
-
- /**********************************/
- /* SetUpSounds */
- /**********************************/
-
- void SetUpSounds (Handle *bufferHandle, short *headerSize)
-
- /* SetUpSounds is a routine which allocates a snd buffer with the proper header
- and sample size, passing the handle and the real header size back to the caller. */
-
- {
- GetSoundDeviceInfo();
-
- /* Allocate largest Handle we could need */
- *bufferHandle = NewHandle(gSampleAreaSize + kEstimatedHeaderSize);
- gError = MemError();
- if (gError != noErr || *bufferHandle == nil)
- ExitWithMessage ("NewHandle failed in SetUpSounds", gError);
-
- /* Set up the header. After this call, we'll know how big the header size is */
- gError = SetupSndHeader (*bufferHandle, gNumberOfChannels, gSampleRate, gSampleSize, gCompression,
- kMiddleC, 0, headerSize);
- if (gError != noErr)
- ExitWithMessage ("SetupSndHeader failed in SetUpSounds", gError);
-
- /* Size the handle down to the size we really need */
- SetHandleSize(*bufferHandle, (Size) *headerSize + gSampleAreaSize);
- gError = MemError();
- if (gError != noErr)
- ExitWithMessage ("SetHandleSize failed in SetUpSounds", gError);
-
- /* Move the handle high and lock it */
- MoveHHi (*bufferHandle);
- gError = MemError();
- if (gError != noErr)
- ExitWithMessage ("MoveHHi failed in SetUpSounds", gError);
-
- HLock (*bufferHandle);
- gError = MemError();
- if (gError != noErr)
- ExitWithMessage ("HLock failed in SetUpSounds", gError);
-
- return;
- }
-
- /**********************************/
- /* FindHeaderSize */
- /**********************************/
-
- int FindHeaderSize (void)
-
- /* This routine returns the number of bytes of the buffer we need to skip
- when calling bufferCmd. The first several bytes of the sound header need to be skipped
- so that the bufferCmd will be pointing at a SoundHeader Record and not an 'snd ' resource
- header. The equations which are used in this routine are from Inside Macintosh VI page 20-22 */
-
- {
- int headerSize;
- short highByte, lowByte;
- short format, numberOfSynths, numberOfCommands;
-
- highByte = **gBufferHandle[0];
- lowByte = *(*gBufferHandle[0] + 1);
- format = (highByte << 8) + lowByte;
-
- switch (format) {
- case 1: /* Format 1 snd */
- headerSize = kBaseHeaderSize;
-
- // find the number of Synths in the snd header
- highByte = *(*gBufferHandle[0] + 2);
- lowByte = *(*gBufferHandle[0] + 3);
- numberOfSynths = (highByte << 8) + lowByte;
- headerSize += numberOfSynths * kSynthSize;
-
- // find the number of commands in the 'snd ' header
- highByte = *(*gBufferHandle[0] + headerSize - 2);
- lowByte = *(*gBufferHandle[0] + headerSize - 1);
- numberOfCommands = (highByte << 8) + lowByte;
- headerSize += numberOfCommands * kCmdSize;
- break;
-
- case 2: /* Format 2 snd */
- headerSize = kBaseHeaderSize;
-
- // find the number of commands in the 'snd ' header
- highByte = *(*gBufferHandle[0] + 4);
- lowByte = *(*gBufferHandle[0] + 5);
- numberOfCommands = (highByte << 8) + lowByte;
- headerSize += numberOfCommands * kCmdSize;
- break;
-
- default:
- break;
- }
-
- return (headerSize);
- }
-
- /**********************************/
- /* BuildRecordStruct */
- /**********************************/
-
- void BuildRecordStruct (Handle bufferHandle)
-
- /* build the gRecordStruct pointer and fill in the fields */
-
- {
- gRecordStruct = (SPBPtr) NewPtr(sizeof (SPB));
- if (gRecordStruct == nil)
- ExitWithMessage ("NewPtr failed in BuildRecordStruct", gError);
-
- gRecordStruct->inRefNum = gSoundRefNum;
- gRecordStruct->count = gSampleAreaSize;
- gRecordStruct->milliseconds = 0;
- gRecordStruct->bufferLength = gSampleAreaSize;
- gRecordStruct->bufferPtr = (Ptr) ((*bufferHandle) + gDataStart);
- gRecordStruct->completionRoutine = (ProcPtr) MyRecComp;
- gRecordStruct->interruptRoutine = nil;
- gRecordStruct->userLong = SetCurrentA5();
- gRecordStruct->error = 0;
- gRecordStruct->unused1 = 0;
-
- return;
- }
-
-
- /**********************************/
- /* ExitWithMessage */
- /**********************************/
-
- void ExitWithMessage (char *message, OSErr error)
-
- /* All errors are passed to this routine to display a message and the error result.
- it also exits the application, because this sample demos the sound manager, not
- error handleing. */
-
- {
- GrafPtr savePort;
- DialogPtr myDialog;
- short itemtype, itemHit;
- Handle itemHand;
- Rect itemRect;
- char *errStrPtr, errStr[256];
-
- errStrPtr = (char *)&errStr;
-
- GetPort(&savePort);
- myDialog = GetNewDialog(kErrorDialogID, nil, (WindowPtr) -1);
- SetPort(myDialog);
-
- NumToString(error,errStrPtr);
- GetDItem(myDialog,kErrNumStatText,&itemtype,&itemHand,&itemRect);
- SetIText(itemHand, errStrPtr);
-
- GetDItem(myDialog,kMsgStatText,&itemtype,&itemHand,&itemRect);
- SetIText(itemHand, message);
-
- do {
- ModalDialog(nil,&itemHit);
- } while (itemHit != kOKButton);
-
- DisposDialog(myDialog);
- SetPort(savePort);
-
- TimeToQuit ();
- }
-
-
- /**********************************/
- /* TimeToQuit */
- /**********************************/
-
- void TimeToQuit (void)
-
- /* Once I am out of the loop it is time to clean up - stop the currently playing sound,
- Dispose of the Channel, close the input driver, and dispose of the Buffer handles and
- gRecordStruct Ptr. */
-
- {
- short index, recordingStat, meterlevel;
- unsigned long totalSamples, numberOfSamples, totalMSec, numberOfMSec;
-
- // check each global to make sure they were allocated before disposing of them
-
- if (gSoundRefNum != 0) {
- gError = SPBGetRecordingStatus (gSoundRefNum, &recordingStat, &meterlevel,
- &totalSamples, &numberOfSamples, &totalMSec, &numberOfMSec);
- if (gError != noErr)
- DebugStr("\SPBGetRecordingStatus failed in TimeToQuit (type 'g' return)");
-
- if (recordingStat > 0) {
- // make sure that recording has stopped before I close the sound driver
- gError = SPBStopRecording (gSoundRefNum);
- if (gError != noErr)
- DebugStr("\pSPBStopRecording failed in TimeToQuit (type 'g' return)");
- }
-
- gError = SPBCloseDevice (gSoundRefNum);
- if (gError != noErr)
- DebugStr("\pSPBCloseDevice failed in TimeToQuit (type 'g' return)");
- }
-
- if (gBufferHandle[0] != nil)
- for (index = 0; index < kNumberOfBuffers; ++index)
- DisposeHandle (gBufferHandle[index]);
-
- if (gRecordStruct != nil)
- DisposePtr ((Ptr) gRecordStruct);
-
- }
-
- /**********************************/
- /* main */
- /**********************************/
-
- void
- InitSoundStuff()
- {
- short index, contOnOff = 1;
- short dataSize;
-
-
- /* Open sound input drive (whichever one is selected in the sound cdev) */
-
- gError = SPBOpenDevice (kDefaultDriver, siWritePermission, &gSoundRefNum);
- if (gError != noErr)
- ExitWithMessage ("SPBOpenDevice failed in main", gError);
-
- /* turn on continuous recording */
-
- gError = SPBSetDeviceInfo (gSoundRefNum,siContinuous, (Ptr) &contOnOff);
- if (gError != noErr)
- ExitWithMessage ("SPBSetDeviceInfo failed in main", gError);
-
- /* build the kNumberOfBuffers snd Buffers */
-
- for (index = 0; index < kNumberOfBuffers; ++index)
- SetUpSounds (&gBufferHandle[index], &gDataStart);
-
- /* determine the part of the header which needs to be skipped before calling bufferCmd */
-
- gHeaderSize = FindHeaderSize();
-
- if (gHeaderSize != 20)
- Debugger();
-
- /* build the gRecordStruct pointer and fill in the fields */
-
- BuildRecordStruct (gBufferHandle[gWhichRecordBuffer]);
-
- /* Build Network Data Buffers */
- gStandardBuffSize = GetHandleSize(gBufferHandle[gWhichRecordBuffer]);
-
- for (index = 0; index < kNumberOfBuffers; ++index)
- gNetBuffers[index] = NewPtr(kNetBufferSize);
-
- gCurrentNetBufferPoint = gNetBuffers[0];
- gCurrentNetBuffer = 0;
- }
-
- void
- StartRecord() {
- gError = SPBRecord (gRecordStruct, true); // start recording
- if (gError != noErr)
- ExitWithMessage ("SPBRecord failed in main", gError);
- }
-
- long
- SendSoundData() {
- short dataSize;
- short extraData;
- long returnedData = 0;
- Ptr sndDataPtr;
- long sndDataLen;
- EventRecord myEvent;
-
- gWaitingToSend = 1;
-
- while ((gWaitingToSend == 1) && (!Button())) {
- WaitNextEvent(0, &myEvent, 0, nil);
- playMovies(&myEvent);
-
- }
-
-
- if (!Button()) {
- gSendingData = 1;
-
- dataSize = gNetBufferSize;
- extraData = gCurrentNetBuffer;
- extraData = NextBuffer (extraData);
- sndDataLen = *((long *) gNetBuffers[extraData]);
- sndDataPtr = gNetBuffers[extraData] + sizeof(long);
- returnedData = AnalizeData((unsigned char *)(sndDataPtr), sndDataLen);
-
- gSendingData = 0;
- }
- else
- {
- extraData = 1;
- dataSize = sizeof(short);
- /* SendData(&dataSize, (Ptr)&extraData); */
- }
- return(returnedData);
- }
-
- void
- SoundCleanUpAndQuit() {
- short dataSize;
-
- gNetBufferSize = 0;
- dataSize = sizeof(short);
- /* SendData(&dataSize, (Ptr)&gNetBufferSize); */
-
- // quitting time
-
- if (gRecordStruct->error < noErr)
- ExitWithMessage ((char *)"\pAn error occurred while recording", gRecordStruct->error);
- else
- TimeToQuit ();
- }
-
- /**********************************/
- /* MyRecComp */
- /**********************************/
-
- pascal void MyRecComp (SPBPtr inParamPtr)
-
- // This is the Completion Routine which is called every time the recording Buffer,
- // is full. This routine needs to setup A5 to be the application's A5 which was being
- // saved in the userLong of the parameter block.
-
- // Due to the MPW C compiler optimization scheme, access to global arrays will be pointed
- // to in an address register as an offset of A5. This will happen before we have a chance
- // to set A5 to our application's A5. To avoid this we need to first restore our A5 and
- // then call the completion routine.
-
- {
- long storeA5;
-
- /* Set A5 so the completion routine has access to the Application Globals */
-
-
- storeA5 = SetA5 (inParamPtr->userLong);
-
- RealCompletion ();
-
- storeA5 = SetA5 (storeA5);
-
- return;
- }
-
- /**********************************/
- /* RealCompletion */
- /**********************************/
-
- void RealCompletion (void)
-
- // Setup the current snd handle to have the correct size of the sample data we've just
- // finished recording. The size of the sample data is in the count field of the
- // recording parameter block. Then play this new buffer of data. Finally, we will
- // start another recording after switching to the other buffer.
-
- {
- OSErr err;
- SoundHeaderPtr header;
-
- // if there has been an error then do nothing
- if (gRecordStruct->error < 0) return;
-
- // get pointer to SoundHeader and update length value in the header
- header = (SoundHeaderPtr) (*(gBufferHandle[gWhichRecordBuffer]) + gHeaderSize);
- header->length = gRecordStruct->count;
-
- /* gNetBuffer = *gBufferHandle[gWhichRecordBuffer]; */
-
- /* If there is enough room, blockmove more in */
- if (((gCurrentNetBufferPoint - gNetBuffers[gCurrentNetBuffer]) + gStandardBuffSize + 1024) <
- kNetBufferSize) {
- BlockMove ((Ptr)&header->length,
- gCurrentNetBufferPoint, sizeof(long));
- gCurrentNetBufferPoint += sizeof(long);
-
- BlockMove (&(header->sampleArea[0]), gCurrentNetBufferPoint, header->length);
- gCurrentNetBufferPoint += gStandardBuffSize;
-
- }
-
- if (gWaitingToSend == 1) {
- gNetBufferSize = (gCurrentNetBufferPoint - gNetBuffers[gCurrentNetBuffer]);
-
- gCurrentNetBuffer = NextBuffer (gCurrentNetBuffer); // move on to next buffer
- gCurrentNetBufferPoint = gNetBuffers[gCurrentNetBuffer];
- gWaitingToSend = 0;
- }
-
- /* PlayBuffer (gBufferHandle[gWhichRecordBuffer]); */ // Play the buffer
-
- gWhichRecordBuffer = NextBuffer (gWhichRecordBuffer); // move on to next buffer
-
- // update gRecordStruct
- gRecordStruct->bufferPtr = (*(gBufferHandle[gWhichRecordBuffer]) + gDataStart);
- gRecordStruct->milliseconds = 0;
- gRecordStruct->count = gSampleAreaSize;
- gRecordStruct->bufferLength = gSampleAreaSize;
-
- err = SPBRecord (gRecordStruct, true); // queue up another record
- if (err)
- DebugStr ("\pSPBRecord died in RealCompletion");
- }
-